// ****************************************************************************
//	Copyright (c) 2011 Daniel Watman
//	http://www.keycommander.net/
// ****************************************************************************
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//	
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************
//	File: detect_focus.cpp
//	Purpose: Detection the application in use
//	Author:	Daniel Watman
//	Created: 2011.02.20
//	Updated: 2011.02.24
//	Version: 0.11
//	Target:	Windows
//	Compiler: Microsoft Visual C++ 2010 Express Edition
// ****************************************************************************
//	Changes:
//		0.1	* Initial version. Can get handle of foreground process, but not always
//		0.11 * Can get PID, name of .exe, title bar text
//			* Timer added to ensure an application change is not missed
// ****************************************************************************
//	To do:
//		* Fix reporting when exe cannot be determined
//		* Detect windows desktop?
//		* Allow selection of running process from list or entry of .exe for detection
//		* Save/load process list to detect
//		* Implement profiles for each process in the list + default
//		* Add KeyCommander functionality (button/macro mapping, image selection)
//		* Add USB communication with KeyCommander hardware
// ****************************************************************************
//	Comments:
//		* This was my first Visual C++ program, it is mashed up from many examples and may be ugly!
// ****************************************************************************
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#include <commctrl.h>
#include <psapi.h>
//#include <string.h>

#pragma comment(lib, "Psapi.lib")	// needed for finding executable name

#define IDC_MAIN_STATUS	101	// status bar
#define ID_TIMER 102		// timer

#define TIMER_INTERVAL 200 //ms (5000 for test, 200ish for real?)

const TCHAR g_szClassName[] = _T("myWindowClass");
UINT TimerID = 0;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		case WM_CREATE:
			{
			HWND hStatus;
			int statwidths[] = {50, 160, -1};	// 3 parts in status bar

			hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
				WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
				hwnd, (HMENU)IDC_MAIN_STATUS, GetModuleHandle(NULL), NULL);

			TimerID = SetTimer(hwnd, ID_TIMER, TIMER_INTERVAL, NULL);	// setup timer

			SendMessage(hStatus, SB_SETPARTS, sizeof(statwidths)/sizeof(int), (LPARAM)statwidths);
			SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)_T("test"));
			}
		break;
		case WM_SIZE:	// window resized
		{
			HWND hStatus;

			// Size status bar
			hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);
			SendMessage(hStatus, WM_SIZE, 0, 0);

		}
		break;
		case WM_TIMER:	// backup for ACTIVATEAPP in case switch is not detected
		case WM_ACTIVATEAPP:	// active application changed, what is it?
		{
			HWND hStatus;
			hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);

			HWND focus_ps;
			TCHAR focus_ps_id[10];
			TCHAR focus_ps_exe[80];
			TCHAR focus_ps_name[200];
			LPDWORD top_PID;
			HMODULE hMod;
			DWORD cbNeeded;

			// find focussed window
			focus_ps = NULL;
			focus_ps=GetForegroundWindow();	// get handle of foreground window
			if (focus_ps == NULL) {
				SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)_T("No window"));
				SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM)_T("No window"));
				SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM)_T("No window"));
			}
			else {
				// get 
				GetWindowThreadProcessId(focus_ps,(LPDWORD)&top_PID);	// get process ID
				wsprintf(focus_ps_id,_T("%u"), top_PID);
				SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)focus_ps_id);	// show PID in status bar (pos 0)

				// need to open process for permission to read exe name
				HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, (DWORD)top_PID );
				if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
					GetModuleBaseName(hProcess, hMod, focus_ps_exe, sizeof(focus_ps_exe)/sizeof(TCHAR));
				SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM)focus_ps_exe);	// show executable name in status bar (pos 1)
				CloseHandle(hProcess);
				
				GetWindowText(focus_ps,focus_ps_name,200);	// get title bar text of the application
				SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM)focus_ps_name);	// show title text in status bar (pos 2)
			}
		}
		break;
		case WM_LBUTTONDOWN:	// just testing, not needed really
		{
			HINSTANCE hInstance = GetModuleHandle(NULL);
			MessageBox(hwnd, _T("CLICK!"),  _T("CLICK!"), MB_OK | MB_ICONINFORMATION);
		}
		break;
		case WM_CLOSE:
			DestroyWindow(hwnd);
		break;
		case WM_DESTROY:
			PostQuitMessage(0);
		break;
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wcex;
	HWND hwnd;
	MSG Msg;

	wcex.cbSize		 = sizeof(WNDCLASSEX);
	wcex.style		 = 0;
	wcex.lpfnWndProc	 = WndProc;
	wcex.cbClsExtra	 = 0;
	wcex.cbWndExtra	 = 0;
	wcex.hInstance	 = hInstance;
	wcex.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
	wcex.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName  = NULL;
	wcex.lpszClassName = g_szClassName;
	wcex.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wcex))
	{
		MessageBox(NULL, _T("Window Registration Failed!"), _T("Error!"),
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	hwnd = CreateWindowEx(
		WS_EX_CLIENTEDGE,
		g_szClassName,
		_T("Foreground Process Detector"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, 600, 200,
		NULL, NULL, hInstance, NULL);

	if(hwnd == NULL)
	{
		MessageBox(NULL, _T("Window Creation Failed!"), _T("Error!"),
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return Msg.wParam;
}
